﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;

namespace Jemgine.Engine
{
    public enum PolygonPhysicsType
    {
        Normal,
        Sensor,
        CenterSensor
    }

    public enum GeometryType
    {
        Physics = 1,
        Draw = 2,
        Both = Physics | Draw,
    }

	[Category("Physics"), Component]
	public class PolygonPhysics : Physics
	{
		public PolygonPhysicsType PhysicsType { get; set; }
		public Box2D.XNA.BodyType Type { get; set; }

		public PolygonPhysics()
		{
            PhysicsType = PolygonPhysicsType.Normal;
			Type = Box2D.XNA.BodyType.Dynamic;
		}

        public override void _Initialize(ContentManager Content)
        {
            if (Me.AttachedElements == null) return;

            PhysicsModule Module = Me.Sim.GetModule<PhysicsModule>();
            if (Module == null) return;

            Box2D.XNA.BodyDef BodyDef = new Box2D.XNA.BodyDef();
            BodyDef.position = Me.Position;
            BodyDef.type = Type;
            BodyDef.bullet = IsBullet;
            BodyDef.allowSleep = AllowSleep;
            //BodyDef.AffectedByGravity = AffectedByGravity;
            BodyDef.linearDamping = LinearDamping;
            _physicsBody = Module.PhysicsSimulator.CreateBody(BodyDef);
            _physicsBody.SetUserData(this);
            _physicsBody.Gravity = (AffectedByGravity ? Gravity : Vector2.Zero);

            Box2D.XNA.MassData MassData = new Box2D.XNA.MassData();


            var fd = new Box2D.XNA.FixtureDef();
            fd.density = 1;
            if (PhysicsType == PolygonPhysicsType.Normal) fd.isSensor = false;
            else fd.isSensor = true;
            fd.filter.groupIndex = (short)CollisionGroup;
            fd.friction = Friction;

            var PolygonShape = new Box2D.XNA.PolygonShape();
            var LoopShape = new Box2D.XNA.LoopShape();
            var CircleShape = new Box2D.XNA.CircleShape();
            var EdgeShape = new Box2D.XNA.EdgeShape();

            foreach (Data.EntityElement Element in Me.AttachedElements)
            {
                fd.shape = null;
                var Primitive = Element as Data.EditorPolygon;
                if (Primitive != null)
                {
                    if ((Primitive.Type & GeometryType.Physics) != GeometryType.Physics) continue;

                    if (Primitive.DecomposedRuntimePolygons != null)
                    {
                        foreach (var Poly in Primitive.DecomposedRuntimePolygons)
                        {
                            PolygonShape.Set(Poly.ToArray(), Poly.Count);
                            fd.shape = PolygonShape;
                            PhysicsBody.CreateFixture(fd);
                            fd.shape = null;
                        }
                    }
                }

                Data.Circle Circle = Element as Data.Circle;
                if (Circle != null)
                {
                    if ((Circle.Type & GeometryType.Physics) != GeometryType.Physics) continue;


                    CircleShape._radius = Circle.Radius;
                    CircleShape._p = Circle.Position;
                    fd.shape = CircleShape;
                }

                Data.Spline Spline = Element as Data.Spline;
                if (Spline != null)
                {
                    if ((Spline.Type & GeometryType.Physics) != GeometryType.Physics) continue;

                    Vector2[] Points = new Vector2[Spline.Segments + 1];

                    float Step = 1.0f / (float)Spline.Segments;
                    float Place = 0.0f;
                    for (int i = 0; i <= Spline.Segments; ++i)
                    {
                        Points[i] = Bezier.Point(Spline[0].Position, Spline[1].Position, Spline[2].Position, Place);
                        Place += Step;
                    }

                    for (int i = 1; i <= Spline.Segments; ++i)
                    {
                        if (i >= 2)
                        {
                            EdgeShape._vertex0 = Points[i - 2];
                            EdgeShape._hasVertex0 = true;
                        }
                        else
                            EdgeShape._hasVertex0 = false;

                        if (i < Spline.Segments)
                        {
                            EdgeShape._vertex3 = Points[i + 1];
                            EdgeShape._hasVertex3 = true;
                        }
                        else
                            EdgeShape._hasVertex3 = false;

                        EdgeShape._vertex1 = Points[i - 1];
                        EdgeShape._vertex2 = Points[i];

                        fd.shape = EdgeShape;
                        _physicsBody.CreateFixture(fd);
                        fd.shape = null;
                    }
                }

                var Line = Element as Data.Line;
                if (Line != null)
                {
                    if ((Line.Type & GeometryType.Physics) != GeometryType.Physics) continue;

                    EdgeShape._hasVertex0 = false;
                    EdgeShape._hasVertex3 = false;
                    EdgeShape._vertex1 = Line[0].Position;
                    EdgeShape._vertex2 = Line[1].Position;
                    fd.shape = EdgeShape;
                }

                if (fd.shape != null)
                    _physicsBody.CreateFixture(fd);
            }

            MassData.mass = Mass;
            MassData.center = Vector2.Zero;
            MassData.I = MassData.mass * MassData.mass / 12;
            _physicsBody.SetMassData(ref MassData);

            base._Initialize(Content);
        }
	}
}
